package model

import (
	"errors"
	"fmt"
	"strings"

	"gitee.com/githubdog/goci/system/db/qbuild"
	"gitee.com/githubdog/goci/tools/helper"
)

// BatchMysqlData 批量更新/添加mysql的数据, 需要相同数量且相同键名的map
// 优化匹配select []string 的字段，否则以数组的第一个键名为主
type BatchMysqlData []map[string]interface{}

type batchMysqlItem struct {
	str  string
	vals []interface{}
}

// GetBatchUpdateString 批量更新的内容: setStr, values, error
func (d *BatchMysqlData) GetBatchUpdateString(table, primary string, selects []string) (string, []interface{}, error) {
	var (
		err                 error
		queryStr            string
		setStrArr, whereArr []string
		data                BatchMysqlData
	)

	allValues := []interface{}{}
	if data, err = d.formatData(primary, selects); err == nil {
		updateArr := map[string]batchMysqlItem{}
		wMap := map[string]bool{}
		keyStr := "," + strings.Join(selects, ",") + ","
		for _, val := range data {
			if fv, ok := val[primary]; ok {
				fStr := ""
				if _fv, ok := fv.(string); ok {
					fStr = "\"" + _fv + "\""
				} else {
					fStr = helper.InterfaceToString(fv)
				}
				if wMap[fStr] {
					err = errors.New("exist two updates of the same primary key")
				} else {
					wMap[fStr] = true
					for k, v := range val {
						if k != primary && (len(selects) == 0 || strings.Contains(keyStr, ","+k+",")) {
							if _, ok := updateArr[k]; !ok {
								updateArr[k] = batchMysqlItem{
									vals: []interface{}{},
								}
							}
							item := updateArr[k]
							if _v, ok := v.(qbuild.RawSQLString); ok {
								item.str += " WHEN " + fStr + " THEN " + string(_v)
							} else {
								item.str += " WHEN " + fStr + " THEN ?"
								item.vals = append(item.vals, v)
							}
							updateArr[k] = item
						}
					}
				}
			}
		}
		for k, v := range updateArr {
			setStrArr = append(setStrArr, qbuild.FormatField(k)+" = CASE "+qbuild.FormatField(primary)+v.str+" END")
			allValues = append(allValues, v.vals...)
		}
		if err == nil {
			for k := range wMap {
				whereArr = append(whereArr, k)
			}
			queryStr = fmt.Sprintf("UPDATE %s SET %s WHERE %s IN (%s)", table, strings.Join(setStrArr, ","), qbuild.FormatField(primary), strings.Join(whereArr, ","))
		}
	}
	return queryStr, allValues, err
}

func (d *BatchMysqlData) formatData(primary string, selects []string) (BatchMysqlData, error) {
	var (
		err       error
		newData   = BatchMysqlData{}
		selectMap = map[string]bool{}
	)

	if data := *d; len(data) > 0 {
		if len(selects) == 0 {
			for k := range data[0] {
				selects = append(selects, k)
			}
		}
		selectMap[primary] = true
		for _, v := range selects {
			selectMap[v] = true
		}
		for key, val := range data {
			for _, v := range selects {
				if _, ok := val[v]; !ok {
					err = fmt.Errorf("miss []map[string]interface{} key data[%d][%s]", key, v)
					return newData, err
				}
			}
			item := map[string]interface{}{}
			for k, v := range val {
				if selectMap[k] {
					item[k] = v
				}
			}
			newData = append(newData, item)
		}
	} else {
		err = errors.New("data is empty")
	}
	return newData, err
}

func (d *BatchMysqlData) GetBatchInsertString(table string, selects []string) (string, []interface{}, error) {
	var (
		err           error
		fields, whArr []string
		queryStr      string
	)

	values := []interface{}{}
	data := *d
	if len(data) > 0 {
		keyStr := "," + strings.Join(selects, ",") + ","
		for k, v := range data[0] {
			if len(selects) == 0 || strings.Contains(keyStr, ","+k+",") {
				fields = append(fields, k)
				if v2, ok := v.(qbuild.RawSQLString); ok {
					whArr = append(whArr, string(v2))
				} else {
					whArr = append(whArr, "?")
				}
			}
		}
		whStr := strings.Join(whArr, ",")
		whArr = []string{}
		for key, val := range data {
			whArr = append(whArr, whStr)
			for _, f := range fields {
				if v, ok := val[f]; ok {
					if _, ok := v.(qbuild.RawSQLString); !ok {
						values = append(values, v)
					}
				} else {
					err = fmt.Errorf("miss []map[string]interface{} key data[%d][%s]", key, f)
					break
				}
			}
		}
		if err == nil {
			for k, v := range fields {
				fields[k] = qbuild.FormatField(v)
			}
		}
	} else {
		err = errors.New("data is empty")
	}
	if err == nil {
		queryStr = fmt.Sprintf(" INTO %s (%s) VALUES (%s)", table, strings.Join(fields, ","), strings.Join(whArr, "),("))
	}
	return queryStr, values, err
}
